Can_Do.php

<?php

namespace Phad;

trait Can_Do {

    public $force_allow_access = false;

    /**
     * Check an access string. 
     * @alias can_access just to make it easier to simply check a string
     * 
     * @param string a string like 'role:admin;call:whatever;'
     * @param $ItemInfo optional array of ItemInfo from a compiled phad item
     *
     * @return true/false
     *
     * @throws exception if a function is not defined.
     */
    public function can(string $function, $ItemInfo=null){
        return $this->can_access(['access'=>$function], $ItemInfo);
    }

    /**
     * Check if an individual row can be read. This is ONLY called when getting an item's rows, NOT when displaying an item's view.
     *
     * @param $ItemRow the data being accessed
     * @param $ItemInfo info info about the item
     * @param $ItemName ... the item's name
     *
     * @return true
     * @override to fine-tune row access
     * @todo allow this to be specified on the item's node
     */
    public function can_read_row(array $ItemRow,object $ItemInfo,string $Item){
        if (isset($this->handlers['can_read_row'])){
            return ($this->handlers['can_read_row'])($ItemRow,$ItemInfo,$Item);
        }
            // echo 'here';exit;
        return true;
    }

    /**
     * @param $node array of the node's attributes & it's tagName
     */
    public function can_read_node(array $node){
        if (!isset($node['access'])){return true;}

       
        return $this->can_access($node);
    }

    /**
     * Checks if the data node can be accessed. 
     *
     * @param $node the data node (as an array)
     * @param $Info item info object
     * @return true/false 
     */
    public function can_read_data(array $node, \stdClass $Info){
        if ($Info->mode==\Phad\Blocks::FORM_SUBMIT&&$node['type']!='default')return false;
        //@deprecated in favor of EPERIMENTAL `:data` arg checked inside the compiled view 
        if (isset($Info->args['data.name'])&&(!isset($node['name'])||$Info->args['data.name']!=$node['name']))return false;
        if (!isset($node['access'])){
            return true;
        }

        return $this->can_access($node);
    }

    /**
     * Check an access string
     * 
     * @param array $node a nodeinfo array containing a key 'access' with a string like 'role:admin;call:whatever;'
     * @param $ItemInfo optional array of ItemInfo from a compiled phad item
     *
     * @return true/false
     *
     * @throws exception if a function is not defined.
     */
    public function can_access(array $node, $ItemInfo=null){
        $access_string = $node['access'];
        $functions = $this->parse_functions($access_string);
        if ($this->force_allow_access === true)return true;

        foreach ($functions as $fn=>/*string*/$arg){
            // var_dump("Function:".$fn);
            if ($fn=='role'){

                if (!$this->user_has_role($arg))return false;
            } else if ($fn=='request_method'){
                if (strtolower($_SERVER['REQUEST_METHOD'])!=strtolower($arg))return false;
            } else if ($fn=='call'){
                //@todo append to `$Info->errors[]` ??
                if($this->call($arg, $node, $ItemInfo)!==true)return false;
            } else {
                // @todo instead of exception, add to errors list ??
                throw new \Exception("Function '$fn' does not exist. Arg was '$arg'");
            }
        }

        return true;
    }


    public function can_delete($Info){
        $candelete = $Info->candelete;
        //@todo test the different branches for can_delete() & delete()
        if (!isset($Info->args['id'])){
            $Info->submit_errors[] = ['msg'=>"Cannot delete '".$Info->name."' without an id. No id was submitted."];
            return false;
        } else if ($candelete===true)return true;
        else if ($candelete===false){
            $Info->submit_errors[] = ['msg'=>"Deletion of '".$Info->name."' is disabled."];
            return false;
        }

        $functions = $this->parse_functions($Info->candelete);
        foreach ($functions as $fn=> /* string */ $args){
            if ($fn=='role'){
                if (!$this->user_has_role($args)){
                    $Info->submit_errors[] = ['msg'=>"Deletion of '".$Info->name."' not allowed unless you have role(s): '$args'"];
                    return false;
                }
            }
            else if ($fn=='call'){
                $count = count($Info->submit_errors);
                $did_pass = $this->call($args, $Info);
                if ($did_pass!==true){
                    if (count($Info->submit_errors)==$count){
                        $item_name = $Info->name;
                        $Info->submit_errors[] = ['msg'=>"Deleting '$item_name' rejected by '$fn:$args'"];
                    }
                    return false;
                }
            }

        }
    
        return true;
    }



    public function can_submit($Info, $ItemRow){
        //@todo test the different branches for can_delete() & delete()
        if (isset($Info->cancel_submit)&&$Info->cancel_submit === true){
            // var_dump($Info->cansubmit);
            return false;
        }

        $functions = $this->parse_functions($Info->cansubmit);
        foreach ($functions as $fn=> /* string */ $args){
            if ($fn=='role'){
                if (!$this->user_has_role($args)){
                    $Info->submit_errors[] = ['msg'=>"Deletion of '".$Info->name."' not allowed unless you have role(s): '$arg'"];
                    return false;
                }
            }
            else if ($fn=='call'){
                $count = count($Info->submit_errors);
                $did_pass = $this->call($args, $Info, $ItemRow);
                if ($did_pass!==true){
                    if (count($Info->submit_errors)==$count){
                        $item_name = $Info->name;
                        $Info->submit_errors[] = ['msg'=>"Deleting '$item_name' rejected by '$fn:$args'"];
                    }
                    return false;
                }
            }

        }
    
        return true;
    }
}